home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 1997 May / PC Plus Super CD Issue 127 (May 1997).iso / delphi1 / lesson4 / tapecalc / calc4.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-07-07  |  12.2 KB  |  452 lines

  1. unit Calc4;
  2. (* Sample Tape Calculator
  3.    For the PC Plus Delphi Tutorial
  4.    Author: Huw Collingbourne
  5.  
  6.    This program implements a simple general purpose calculator
  7.    with a scrollable list box displaying the caklculations.
  8.    This list can be saved to disk for future reference.
  9.  
  10.    Defects (which you may like to correct!)
  11.    Currently the calculator...
  12.    - cannot load a saved list (you can use Notepad for this)
  13.    - does no IO checking
  14.    - does not trap errors such as divide by 0 or floating-point overflow
  15.    - has limited editing capabilities in the DisplayEd text edit box
  16.  
  17.  
  18.    Note: most of these features have already been implemented in earlier
  19.    programs in the PC Plus Delphi tutorial series. If you want to add them
  20.    to this application, refer to our other samples.
  21.  
  22.    You may also like to make the program more object orientated by binding the
  23.    'general purpose' routines such as InputError into the form-level class.
  24.    This is discussed in part 4 of the Delphi tutorial.
  25.    *)
  26.  
  27.  
  28. interface
  29.  
  30. uses
  31.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  32.   Forms, Dialogs, StdCtrls, Menus;
  33.  
  34. type
  35.   TCalcForm = class(TForm)
  36.     DisplayEd: TEdit;
  37.     Btn8: TButton;
  38.     Btn6: TButton;
  39.     Btn4: TButton;
  40.     Btn2: TButton;
  41.     Btn0: TButton;
  42.     Btn9: TButton;
  43.     Btn7: TButton;
  44.     Btn5: TButton;
  45.     Btn3: TButton;
  46.     Btn1: TButton;
  47.     BtnEquals: TButton;
  48.     BtnDiv: TButton;
  49.     BtnMult: TButton;
  50.     BtnMinus: TButton;
  51.     BtnPlus: TButton;
  52.     BtnDot: TButton;
  53.     ClearBtn: TButton;
  54.     CalcList: TListBox;
  55.     MainMenu1: TMainMenu;
  56.     FileMenu: TMenuItem;
  57.     ClearMenuItem: TMenuItem;
  58.     SaveMenuItem: TMenuItem;
  59.     procedure Btn0Click(Sender: TObject);
  60.     procedure Btn1Click(Sender: TObject);
  61.     procedure Btn2Click(Sender: TObject);
  62.     procedure Btn3Click(Sender: TObject);
  63.     procedure Btn4Click(Sender: TObject);
  64.     procedure Btn5Click(Sender: TObject);
  65.     procedure Btn6Click(Sender: TObject);
  66.     procedure Btn7Click(Sender: TObject);
  67.     procedure Btn8Click(Sender: TObject);
  68.     procedure Btn9Click(Sender: TObject);
  69.     procedure ClearBtnClick(Sender: TObject);
  70.     procedure FormActivate(Sender: TObject);
  71.     procedure BtnPlusClick(Sender: TObject);
  72.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  73.     procedure BtnEqualsClick(Sender: TObject);
  74.     procedure BtnDivClick(Sender: TObject);
  75.     procedure BtnMultClick(Sender: TObject);
  76.     procedure BtnMinusClick(Sender: TObject);
  77.     procedure BtnDotClick(Sender: TObject);
  78.     procedure FormCreate(Sender: TObject);
  79.     procedure FormKeyPress(Sender: TObject; var Key: Char);
  80.     procedure FormKeyDown(Sender: TObject; var Key: Word;
  81.       Shift: TShiftState);
  82.     procedure FormKeyUp(Sender: TObject; var Key: Word;
  83.       Shift: TShiftState);
  84.     procedure ClearMenuItemClick(Sender: TObject);
  85.     procedure SaveMenuItemClick(Sender: TObject);
  86. private
  87.     { Private declarations }
  88.     EnterNewFigures: boolean;{ flag if new number is being entered }
  89.     CalcNum : integer;       { keep a check on num of calcs done   }
  90.     { The following Methods aren't event-handlers and are
  91.     not bound to specific visual objects }
  92.     procedure UpdateResult( newOp : char );
  93.     procedure AppendNumber( numCh : char );
  94.     procedure ReInit;
  95.  
  96.   public
  97.     { Public declarations }
  98.  
  99.   end;
  100.  
  101. { TMemory is a non-visual class which simply stores the previous value
  102. which appeared in the calculator's edit box. Say, for example, we had
  103. a TMemory object called PrevVal, we can now pick and operator, such
  104. as '+' and then enter a new value, called NewVal. When we press the
  105. '=' button (or another operator button such as '+' or '-'), the edit
  106. box can be updated to show the total of PrevVal + NewVal ).    }
  107. TMemory = class(TObject)
  108.   total : real;
  109.   function gettotal : real;
  110.   procedure settotal( r : real );
  111. end;
  112.  
  113.  
  114. { Stores the currently selected operator such as '+' or '-' }
  115. TOperation = class(TObject)
  116.   op : char;
  117.   function getop : char;
  118.   procedure setop( c : char );
  119. end;
  120.  
  121.  
  122. var
  123.   CalcForm: TCalcForm;
  124.   LastResult : TMemory;
  125.   LastOp : TOperation;
  126.  
  127. implementation
  128.  
  129. {$R *.DFM}
  130.  
  131. { Methods of the TMemory class. Set and read the internal variable }
  132. function TMemory.gettotal : real;
  133. begin
  134.  gettotal := total;
  135. end;
  136.  
  137. procedure TMemory.settotal( r : real );
  138. begin
  139.  total := r;
  140. end;
  141.  
  142.  
  143.  
  144. { Methods of the TOperation class }
  145. function TOperation.getop : char;
  146. begin
  147.  getop := op;
  148. end;
  149.  
  150. procedure TOperation.setop( c : char );
  151. begin
  152.  op := c;
  153. end;
  154.  
  155. { ------------- general-purpose routines ---------------- }
  156.  
  157. { warn user if an erroneous value has been entered - e.g. '1..5' and
  158. allows them to edit the value before continuing }
  159. procedure InputError( TE: TEdit; errcode : integer );
  160. var
  161.    Msg : string;
  162. begin
  163.     if TE.Text = '' Then
  164.        Msg := 'You must enter a value'
  165.     else
  166.        Msg :=  'Invalid character: ' + Copy(TE.Text, errcode, 1);
  167.     MessageDlg(Msg, mtError,
  168.             [mbOk], 0);
  169.     TE.SetFocus;
  170.     TE.SelStart := errcode-1;
  171.     TE.SelLength := 1;
  172. end;
  173.  
  174. { checks to see if the value in the edit box is valid. if so,
  175. the value is returned in the variable, realValue and the function
  176. returns True. Otherwise, it returns false }
  177. function CurrentNumberOK( TE: TEdit; var realValue : real ) : boolean;
  178. var
  179.  rv : real;
  180.  errcode : integer;
  181. begin
  182.    Val(TE.Text, rv, errcode);
  183.    if errcode = 0 then
  184.    begin
  185.       realValue := rv;
  186.       CurrentNumberOK := true;
  187.    end
  188.    else
  189.    begin
  190.       InputError(TE, errcode );
  191.       CurrentNumberOK := false;
  192.    end;
  193. end;
  194.  
  195. { --- TCacForm methods --- }
  196. procedure TCalcForm.ReInit;
  197. { Clear memory, clear edit field }
  198. begin
  199.   DisplayEd.Text := '';
  200.   LastResult.settotal(0.0);
  201.   LastOp.setOp('+');
  202.   EnterNewFigures := true;
  203.   CalcNum := 0;
  204. end;
  205.  
  206. procedure TCalcForm.UpdateResult( newOp : char );
  207. { When an operator (newOp) is chosen, this method performs the current
  208. calculation and updates the Op field of the LastOp object so that
  209. this is avalable for use in the current calculation }
  210. var
  211.    lastNum : real;
  212.    lastOperator : char;
  213.    newNum : real;
  214.    total : real;
  215.    strLastNum, strNewNum, strTotal : string;
  216.    calcStr : string; { string representation of the entire expression }
  217.    showTotal : boolean;
  218. begin
  219.   newNum := 0.0;
  220.   showTotal := false;
  221.  
  222. { The code in this method only executes if the contents of the
  223.   edit field are valid. If an error is encountered, nothing is done.
  224.   This gives the user the chance to correct the error before
  225.   continuing }
  226.   if CurrentNumberOK( DisplayEd, newNum ) then
  227.   begin
  228.       { retrieve the previous value and operator needed for this
  229.       calculation }
  230.     lastNum := LastResult.gettotal;
  231.     lastOperator := LastOp.getop;
  232.     { use a CASE statment to select the appropriate calculation }
  233.     case lastOperator of
  234.       '+': total := lastNum + newNum;
  235.       '-': total := lastNum - newNum;
  236.       '/': total := lastNum / newNum;
  237.       '*': total := lastNum * newNum;
  238.       '=': begin
  239.              total := lastNum;
  240.              ShowTotal := true;
  241.            end;
  242.       else total := lastNum;
  243.     end;
  244.     { Convert the real value, total, to the string value,
  245.       strTotal and display it in the edit box }
  246.     Str(total:2:2, strTotal );
  247.     DisplayEd.Text := strTotal;
  248.  
  249.     Str(lastNum:2:2, strLastNum);
  250.     Str(newNum:2:2, strNewNum);
  251.  
  252.     { add the calculation to the list box }
  253.     { we don't need to do anything if '=' was selected, since the
  254.     total is shown automatically. }
  255.     if not ShowTotal then
  256.     begin
  257.     { We don't need to show anything if this is the first number
  258.     that's been entered (calcNum = 0) }
  259.        if calcNum > 0 then
  260.        begin
  261.          calcStr := strLastNum + ' ' + lastOperator + ' '
  262.                         +  strNewNum + ' = ' + strTotal;
  263.          CalcList.Items.Add( calcStr  );  (* XXX *)
  264.          { force the list box to scroll to show newest item at }
  265.          { the last visible line in the box }
  266.          CalcList.TopIndex := CalcList.Items.Count - 1;
  267.        end;
  268.        Inc( CalcNum ) { add 1 to CalcNum }
  269.     end;
  270.  
  271.  
  272.     { update the lastOp and lastResult objects,
  273.       ready for the next calculation }
  274.     lastOp.setOp( newOp );
  275.     lastResult.settotal(total);
  276.  
  277.     { set the EnterNewFigures variable to true. This is used in the
  278.     AppendNumber method }
  279.     EnterNewFigures := true;
  280.   end;
  281. end;
  282.  
  283. procedure TCalcForm.AppendNumber( numCh : char );
  284. { If a calculation has just been completed, the EnterNewFigures
  285.   variable is True. So the edit box is cleared to let the user
  286.   start entering a new number. Otherwise, digits are appended
  287.   to the contents of the edit box }
  288. begin
  289.    if EnterNewFigures = true then
  290.    begin
  291.       DisplayEd.Text := '';
  292.       EnterNewFigures := false;
  293.    end;
  294.       DisplayEd.Text := DisplayEd.Text + numCh;
  295. end;
  296.  
  297. { the form's event-handling code }
  298. { Each button sends a number to be added to the edit box }
  299. procedure TCalcForm.Btn0Click(Sender: TObject);
  300. begin
  301.      AppendNumber( '0' );
  302. end;
  303.  
  304. procedure TCalcForm.Btn1Click(Sender: TObject);
  305. begin
  306.      AppendNumber( '1' );
  307. end;
  308.  
  309. procedure TCalcForm.Btn2Click(Sender: TObject);
  310. begin
  311.      AppendNumber( '2' );
  312. end;
  313.  
  314. procedure TCalcForm.Btn3Click(Sender: TObject);
  315. begin
  316.      AppendNumber( '3' );
  317. end;
  318.  
  319. procedure TCalcForm.Btn4Click(Sender: TObject);
  320. begin
  321.      AppendNumber( '4' );
  322. end;
  323.  
  324. procedure TCalcForm.Btn5Click(Sender: TObject);
  325. begin
  326.      AppendNumber( '5' );
  327. end;
  328.  
  329. procedure TCalcForm.Btn6Click(Sender: TObject);
  330. begin
  331.      AppendNumber( '6' );
  332. end;
  333.  
  334. procedure TCalcForm.Btn7Click(Sender: TObject);
  335. begin
  336.      AppendNumber( '7' );
  337. end;
  338.  
  339. procedure TCalcForm.Btn8Click(Sender: TObject);
  340. begin
  341.      AppendNumber( '8' );
  342. end;
  343.  
  344. procedure TCalcForm.Btn9Click(Sender: TObject);
  345. begin
  346.      AppendNumber( '9' );
  347. end;
  348.  
  349. procedure TCalcForm.ClearBtnClick(Sender: TObject);
  350. begin
  351.   ReInit;
  352. end;
  353.  
  354. procedure TCalcForm.FormActivate(Sender: TObject);
  355. { When the calculator is first run, we create the two
  356. objects, LastResult and LastOp and call ReInit to do some
  357. setup tasks }
  358. begin
  359.   LastResult := TMemory.Create;
  360.   LastOp := TOperation.Create;
  361.   ReInit;
  362. end;
  363.  
  364.  
  365. procedure TCalcForm.FormClose(Sender: TObject; var Action: TCloseAction);
  366. { When the calculator is closed, we 'clean up' by destroying the objects
  367. we created in the FormActivate method }
  368. begin
  369.  LastResult.Free;
  370.  LastOp.Free;
  371. end;
  372.  
  373. { The operator buttons }
  374.  
  375. procedure TCalcForm.BtnEqualsClick(Sender: TObject);
  376. begin
  377.      UpdateResult( '=' );
  378. end;
  379.  
  380. procedure TCalcForm.BtnDivClick(Sender: TObject);
  381. begin
  382.      UpdateResult( '/' );
  383. end;
  384.  
  385. procedure TCalcForm.BtnMultClick(Sender: TObject);
  386. begin
  387.      UpdateResult( '*' );
  388. end;
  389.  
  390. procedure TCalcForm.BtnPlusClick(Sender: TObject);
  391. begin
  392.      UpdateResult( '+' );
  393. end;
  394.  
  395. procedure TCalcForm.BtnMinusClick(Sender: TObject);
  396. begin
  397.      UpdateResult( '-' );
  398. end;
  399.  
  400. procedure TCalcForm.BtnDotClick(Sender: TObject);
  401. begin
  402.      AppendNumber( '.' );
  403. end;
  404.  
  405. procedure TCalcForm.FormCreate(Sender: TObject);
  406. { The KeyPreview property lets the Form examine characters
  407.   even when other controls may have the focus }
  408. begin
  409.   KeyPreview := True;
  410. end;
  411.  
  412. procedure TCalcForm.FormKeyPress(Sender: TObject; var Key: Char);
  413. { The form can accept the same characters via keyboard input as
  414.   via the on-screen buttons. The same procedures are called }
  415. begin
  416.   case (Key) of
  417.   '0'..'9', '.' : AppendNumber(Key);
  418.   '+', '-', '/', '*', '=' : UpdateResult(Key);
  419.   end;
  420. end;
  421.  
  422.  
  423.  
  424. procedure TCalcForm.FormKeyDown(Sender: TObject; var Key: Word;
  425.   Shift: TShiftState);
  426. { Handle 'virtual keys' - specifically Del and Backspace }
  427. begin
  428.   if (Key = VK_Delete) or (Key = VK_Back) then
  429.      DisplayEd.ReadOnly := False; { let the edit box accept these keys }
  430.   end;
  431.  
  432. procedure TCalcForm.FormKeyUp(Sender: TObject; var Key: Word;
  433.   Shift: TShiftState);
  434. { make sure the edit box is set back to ReadOnly when the
  435.   current keystroke has been processed
  436.  (see procedure TCalcForm.FormKeyDown) }
  437. begin
  438.   DisplayEd.ReadOnly := True;
  439. end;
  440.  
  441. procedure TCalcForm.ClearMenuItemClick(Sender: TObject);
  442. begin
  443.   CalcList.Clear;
  444. end;
  445.  
  446. procedure TCalcForm.SaveMenuItemClick(Sender: TObject);
  447. begin
  448.   CalcList.Items.SaveToFile('CalcList.TXT');
  449. end;
  450.  
  451. end.
  452.